// Copyright (c) 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_

#include <assert.h>
#include <sys/types.h>

#include <string>
#include <jni.h>

#include "client/linux/handler/microdump_extra_info.h"
#include "common/using_std_string.h"

// This class describes how a crash dump should be generated, either:
// - Writing a full minidump to a file in a given directory (the actual path,
//   inside the directory, is determined by this class).
// - Writing a full minidump to a given fd.
// - Writing a reduced microdump to the console (logcat on Android).
namespace google_breakpad {

    class MinidumpDescriptor {
    public:
        struct MicrodumpOnConsole {
        };
        static const MicrodumpOnConsole kMicrodumpOnConsole;
        struct MinidumpJustJava {
        };
        static const MinidumpJustJava kMinidumpJustJava;

        MinidumpDescriptor()
                : mode_(kUninitialized),
                  fd_(-1),
                  size_limit_(-1),
                  address_within_principal_mapping_(0),
                  skip_dump_if_principal_mapping_not_referenced_(false) {}

        explicit MinidumpDescriptor(const string &directory)
                : mode_(kWriteMinidumpToFile),
                  fd_(-1),
                  directory_(directory),
                  c_path_(NULL),
                  size_limit_(-1),
                  address_within_principal_mapping_(0),
                  skip_dump_if_principal_mapping_not_referenced_(false),
                  sanitize_stacks_(false) {
            assert(!directory.empty());
        }

        explicit MinidumpDescriptor(int fd)
                : mode_(kWriteMinidumpToFd),
                  fd_(fd),
                  c_path_(NULL),
                  size_limit_(-1),
                  address_within_principal_mapping_(0),
                  skip_dump_if_principal_mapping_not_referenced_(false),
                  sanitize_stacks_(false) {
            assert(fd != -1);
        }

        explicit MinidumpDescriptor(const MicrodumpOnConsole &)
                : mode_(kWriteMicrodumpToConsole),
                  fd_(-1),
                  size_limit_(-1),
                  address_within_principal_mapping_(0),
                  skip_dump_if_principal_mapping_not_referenced_(false),
                  sanitize_stacks_(false) {}

        explicit MinidumpDescriptor(const MinidumpJustJava &)
                : mode_(kWriteMicrodumpJustToJava),
                  fd_(-1),
                  c_path_(NULL),
                  size_limit_(-1),
                  address_within_principal_mapping_(0),
                  skip_dump_if_principal_mapping_not_referenced_(false),
                  sanitize_stacks_(false) {
        }


        explicit MinidumpDescriptor(const MinidumpDescriptor &descriptor);

        MinidumpDescriptor &operator=(const MinidumpDescriptor &descriptor);

        static MinidumpDescriptor getMicrodumpDescriptor();

        bool IsFD() const { return mode_ == kWriteMinidumpToFd; }

        int fd() const { return fd_; }

        bool IsFile() const { return mode_ == kWriteMinidumpToFile; }

        string directory() const { return directory_; }

        const char *path() const { return c_path_; }

        bool IsMicrodumpOnConsole() const {
            return mode_ == kWriteMicrodumpToConsole;
        }

        // Updates the path so it is unique.
        // Should be called from a normal context: this methods uses the heap.
        void UpdatePath();

        off_t size_limit() const { return size_limit_; }

        void set_size_limit(off_t limit) { size_limit_ = limit; }

        uintptr_t address_within_principal_mapping() const {
            return address_within_principal_mapping_;
        }

        void set_address_within_principal_mapping(
                uintptr_t address_within_principal_mapping) {
            address_within_principal_mapping_ = address_within_principal_mapping;
        }

        bool skip_dump_if_principal_mapping_not_referenced() {
            return skip_dump_if_principal_mapping_not_referenced_;
        }

        void set_skip_dump_if_principal_mapping_not_referenced(
                bool skip_dump_if_principal_mapping_not_referenced) {
            skip_dump_if_principal_mapping_not_referenced_ =
                    skip_dump_if_principal_mapping_not_referenced;
        }

        bool sanitize_stacks() const { return sanitize_stacks_; }

        void set_sanitize_stacks(bool sanitize_stacks) {
            sanitize_stacks_ = sanitize_stacks;
        }

        MicrodumpExtraInfo *microdump_extra_info() {
            assert(IsMicrodumpOnConsole());
            return &microdump_extra_info_;
        }

    private:
        enum DumpMode {
            kUninitialized = 0,
            kWriteMinidumpToFile,
            kWriteMinidumpToFd,
            kWriteMicrodumpToConsole,
            kWriteMicrodumpJustToJava,
        };

        // Specifies the dump mode (see DumpMode).
        DumpMode mode_;

        // The file descriptor where the minidump is generated.
        int fd_;

        // The directory where the minidump should be generated.
        string directory_;

        // The full path to the generated minidump.
        string path_;

        // The C string of |path_|. Precomputed so it can be access from a compromised
        // context.
        const char *c_path_;

        off_t size_limit_;

        // This member points somewhere into the main module for this
        // process (the module that is considerered interesting for the
        // purposes of debugging crashes).
        uintptr_t address_within_principal_mapping_;

        // If set, threads that do not reference the address range
        // associated with |address_within_principal_mapping_| will not have their
        // stacks logged.
        bool skip_dump_if_principal_mapping_not_referenced_;

        // If set, stacks are sanitized to remove PII. This involves
        // overwriting any pointer-aligned words that are not either
        // pointers into a process mapping or small integers (+/-4096). This
        // leaves enough information to unwind stacks, and preserve some
        // register values, but elides strings and other program data.
        bool sanitize_stacks_;

        // The extra microdump data (e.g. product name/version, build
        // fingerprint, gpu fingerprint) that should be appended to the dump
        // (microdump only). Microdumps don't have the ability of appending
        // extra metadata after the dump is generated (as opposite to
        // minidumps MIME fields), therefore the extra data must be provided
        // upfront. Any memory pointed to by members of the
        // MicrodumpExtraInfo struct must be valid for the lifetime of the
        // process (read: the caller has to guarantee that it is stored in
        // global static storage.)
        MicrodumpExtraInfo microdump_extra_info_;
    };

}  // namespace google_breakpad

#endif  // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
